////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Log.print('service module: sinedria');

// Κάθε ενεργός παίκτης έχει ένα αντίστοιχο record συνεδρίας στον πίνακα "sinedria".
// Ισχύει και το αντίστροφο, δηλαδή σε κάθε record συνεδρίας αντιστοιχεί ένας ενεργός
// παίκτης. Αυτό το τελευταίο δεν είναι απαραίτητο ότι απεικονίζει και την πραγματικότητα,
// με την έννοια ότι μπορεί κάποιος παίκτης να κλείσει τη σελίδα με την οποία επικοινωνεί
// με τον server χωρίς προηγουμένως να έχει ζητήσει ρητά την αποχώρησή του από το καφενείο.
// Σε αυτές τις περιπτώσεις ενώ υπάρχει record συνεδρίας, ο παίκτης δεν βρίσκεται online
// και θα φαίνεται ενεργός μέχρι το πρόγραμμα περιοδικού ελέγχου ανενεργών συνεδριών να
// εκκαθαρίσει το συγκεκριμένο record συνεδρίας.
//
// Πράγματι, κάθε φορά που ο παίκτης επικοινωνεί με τον server μέσω της βασικής σελίδας,
// ενημερώνεται το πεδίο "poll" του αντίστοιχου record συνεδρίας. Ακόμη και αν ο παίκτης
// δεν κάνει οποιαδήποτε ενέργεια, το πεδίο "poll" ενημερώνεται σε τακτική βάση (της τάξης
// κάποιων δεκάδων δευτερολέπτων). Συνεπώς το πρόγραμμα περιοδικού ελέγχου μπορεί να εντοπίσει,
// να αρχειοθετήσει και τελικώς να διαγράψει παρωχημένα records συνεδριών.
//
// Από τεχνικής απόψεως οι συνεδρίες υλοποιούνται στον Node server ως associative array
// με δείκτες τα login names των παικτών. Το array αυτό εντάσσεται στο αντικείμενο
// "Skiniko" στο οποίο εντάσσονται και άλλα παρόμοια arrays που αφορούν στην τρέχουσα
// κατάσταση του καφενείου (τραπέζια, διανομές, κινήσεις κλπ). Πράγματι, υπάρχει
// το associative array "sinedria" ως property του αντικειμένου "Skiniko", το οποίο
// περιέχει τις συνεδρίες.

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Τα σχετικά με τις υπηρεσίες που ορίζονται στο παρόν module εντάσσονται στο
// αντικείμενο "sinedria" το οποίο χρησιμοποιείται ως name space.

Service.sinedria = {};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Το service "sinedriaNea" καλείται αμέσως μετά την είσοδο ή την εγγραφή του παίκτη.
// Πράγματι, κατά την εγγραφή νέου παίκτη έχουμε Ajax κλήση με την οποία δημιουργείται
// το record του παίκτη και συνακόλουθο record συνεδρίας, ενώ κατά την είσοδο του παίκτη
// δημιουργείται μόνο record συνεδρίας. Και στις δύο περιπτώσεις επιστρέφεται το πεδίο
// "klidi" του νεότευκτου record συνεδρίας, το οποίο χρησιμοποιείται αμέσως μετά ως
// παράμετρος σε Node κλήση προς το παρόν service και με το οποίο προστίθεται η συνεδρία
// στο array συνεδριών του σκηνικού του Node server.

Service.sinedria.nea = function(req) {
	if (req.anonimo('Ακαθόριστος παίκτης κατά τη δημιουργία νέας συνεδρίας')) return;

	var conn = DB.connection();
	query = 'SELECT `pektis`, `klidi`, `ip`, UNIX_TIMESTAMP(`isodos`) AS `isodos` ' +
		'FROM `sinedria` WHERE (`pektis` = BINARY ' + conn.escape(req.url.pektis) +
		') AND (`klidi` = BINARY ' + conn.escape(req.url.klidi) + ') LIMIT 1';
	conn.connection.query(query, function(err, rows) {
		conn.free();
		if ((!rows) || (!rows.hasOwnProperty('length')) || rows.length != 1)
			return req.end('Δεν βρέθηκε συνεδρία για τον παίκτη ' +
				'"<span class="entono ble">' + req.url.pektis + '</span>"');

		// Παράλληλα με την ένταξη νέας συνεδρίας στο σκηνικό, «φορτώνουμε» και τον
		// παίκτη στο αντίστοιχο associative array του σκηνικού. Πράγματι, τα στοιχεία
		// των παικτών δεν πρέπει να φορτώνονται στις ίδιες τις συνεδρίες, καθώς μπορεί
		// να χρειαστούμε στοιχεία παικτών για παίκτες που δεν βρίσκονται online και
		// συνεπώς δεν έχουν σχετικές συνεδρίες.
 
		Service.pektis.load(req.url.pektis, function(pektis) {
			(new Sinedria()).rowSet(rows[0]).entaxi();
			req.end();
		}, function(login) {
			req.end('Δεν βρέθηκαν στοιχεία για τον παίκτη "<span class="entono ble">' +
				login + '</span>"');
		});
	});
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Κατά την έξοδο, εκτός από την ενημέρωση του σκηνικού και την συνακόλουθη
// ενημέρωση των clients, καταργούμε και τυχόν ανοικτό αίτημα "feredata"
// για τον εν λόγω παίκτη.

Service.sinedria.exodos = function(req) {
	if (req.isvoli()) return;
	req.sinedria.apentaxi();
	req.end();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Service.sinedria.trapeziIsodos = function(req) {
	var trapezi;

	if (req.isvoli()) return;
	if (!req.url.hasOwnProperty('trapezi')) return req.end('Ακαθόριστο τραπέζι');

	trapezi = Skiniko.isTrapezi(req.url.trapezi);
	if (!trapezi) return req.end('Δεν υφίσταται το τραπέζι');
	trapezi.trapeziEpafi();

	Service.sinedria.trapeziIsodos2(req, trapezi);
}

// Ακολουθεί το δεύτερο τμήμα της διαδικασίας εισόδου σε τραπέζι. Η διαδικασία έχει
// σπάσει σε δύο τμήματα καθώς το δεύτερο τμήμα μπορεί να κληθεί εμμέσως και από
// αλλού, π.χ. από την αποδοχή πρόσκλησης σε τραπέζι που ήδη είμαστε ενταγμένοι ως
// παίκτες.

Service.sinedria.trapeziIsodos2 = function(req, trapezi) {
	req.end();
	req.sinedria.trapeziIsodos(trapezi);

	(new Kinisi({
		'?': 'IS',
		'p': req.sinedria.pektis,
		't': trapezi.kodikos,
	})).kinisiAdd();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Service.sinedria.trapeziExodos = function(req, checked) {
	var conn;

	if (!checked) {
		if (req.isvoli()) return;
		if (req.oxiTrapezi()) return;
	}

	conn = DB.connection();
	conn.transaction(function(conn) {
		Service.sinedria.trapeziExodos1(conn, req);
	});
}

Service.sinedria.trapeziExodos1 = function(conn, req) {
	var query = 'DELETE FROM `prosklisi` WHERE `trapezi` = ' + req.sinedria.trapezi;
	conn.query(query, function(con) {
		Service.sinedria.trapeziExodos2(conn, req);
	});
}

Service.sinedria.trapeziExodos2 = function(conn, req) {
	var query = 'UPDATE `trapezi` SET `arxio` = NOW() WHERE `kodikos` = ' + req.sinedria.trapezi;
	conn.query(query, function(con) {
		if (conn.affectedRows != 1) {
			conn.rollback();
			return req.end('Απέτυχε η αρχειοθέτηση του τραπεζιού');
		}

		conn.commit();
		Service.sinedria.trapeziExodos3(req);
	});
}

Service.sinedria.trapeziExodos3 = function(req) {
	req.end();
	req.sinedria.simetoxiSave();
	req.sinedria.trapeziExodos();
	new Kinisi({
		'?': 'EX',
		'p': req.sinedria.pektis,
	}).kinisiAdd();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Service.sinedria.pektisTheatis = function(req) {
	if (req.isvoli()) return;
	if (req.oxiTrapezi()) return;

	req.trapezi.trapeziEpafi();
	if (req.trapezi.kenes() == 2) return Service.sinedria.trapeziExodos(req, true);

	req.end();
	req.sinedria.simetoxiSave();
	req.sinedria.pektisTheatis();
	(new Kinisi({
		'?': 'P2H',
		'p': req.sinedria.pektis,
	})).kinisiAdd();
}

Service.sinedria.theatisPektis = function(req) {
	var thesi, conn;

	if (req.isvoli()) return;
	if (req.oxiTrapezi()) return;

	req.trapezi.trapeziEpafi();
	thesi = req.trapezi.thesiEpilogi(req.sinedria.pektis, true);
	if (!thesi) return req.end('Δεν υπάρχει θέση στο τραπέζι');

	conn = DB.connection();
	conn.transaction(function(conn) {
		Service.sinedria.theatisPektis1(conn, req, thesi);
	});
}

Service.sinedria.theatisPektis1 = function(conn, req, thesi) {
	var query = 'UPDATE `trapezi` SET `pektis' + thesi + '` = ' + conn.escape(req.sinedria.pektis) +
		' WHERE `kodikos` = ' + req.sinedria.trapezi;
	conn.query(query, function(conn) {
		if (!req.sinedria.theatisPektis(thesi)) {
			conn.rollback();
			return req.end('Απέτυχε η αλλαγή θεατού σε παίκτη');
		}

		req.end();
		conn.commit();
		(new Kinisi({
			'?': 'H2P',
			'p': req.sinedria.pektis,
			'h': thesi,
		})).kinisiAdd();
	});
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η property "timeout" είναι το μέγιστο επιτρεπτό διάστημα για το οποίο μια
// συνεδρία μπορεί να μην έχει επαφή με το σύστημα. Αν κατά τον περιοδικό
// έλεγχο των συνεδριών κάποια συνεδρία υπερβαίνει το διάστημα αυτό, τότε
// η συνεδρία κλείνει. Η τιμή θα υπολογιστεί κατά το στήσιμο των περιπόλων.

Service.sinedria.timeout = 0;

Service.sinedria.check = function() {
console.log('Service.sinedria: check (' + globals.ora() + ')');
	var tora = globals.toraServer();
	Skiniko.sinedriaWalk(function(login, sinedria) {
		Skiniko.fetchPektis(login).pektisEpafi();
		if (tora - sinedria.poll < Service.sinedria.timeout) return;
console.log('Service.sinedria timeout: ' + login);
		Service.sinedria.purge(login);
	});
}

Service.sinedria.purge = function(pektis) {
	var sinedria = Skiniko.isSinedria(pektis);
	if (!sinedria) return;

	var conn = DB.connection();
	conn.query('START TRANSACTION', function(conn) {
		Service.sinedria.purge1(conn, sinedria);
	});
}

Service.sinedria.purge1 = function(conn, sinedria) {
	var spektis = conn.escape(sinedria.pektis);
	query = 'INSERT INTO `istoriko` (`pektis`, `ip`, `isodos`, `exodos`) VALUES ' +
		'(' + spektis + ", '" + sinedria.ip + "', FROM_UNIXTIME(" + sinedria.isodos +
		"), FROM_UNIXTIME(" + sinedria.poll + "))";
	conn.connection.query(query, function(err, res) {
		if (err) {
			conn.rollback();
			Log.sqlError(query);
			return;
		}

		Service.sinedria.purge2(conn, sinedria, spektis);
	});
}

Service.sinedria.purge2 = function(conn, sinedria, spektis) {
	var query = 'DELETE FROM `sinedria` WHERE `pektis` = BINARY ' + spektis;
	conn.connection.query(query, function(err, res) {
		if (err) {
			conn.rollback();
			Log.sqlError(query);
			return;
		}

		conn.commit();
		sinedria.apentaxi();
	});
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Service.sinedria.thesiTheasis = function(req) {
	if (req.isvoli()) return;
	if (!req.url.hasOwnProperty('thesi')) return req.end('Ακαθόριστη θέση');
	if (Prefadoros.oxiThesi(req.url.thesi)) return req.end('Λανθασμένη θέση');
	if (req.sinedria.oxiTheatis()) return req.end();

	req.end();
	req.sinedria.thesiTheasis(req.url.thesi);
	(new Kinisi({
		'?': 'HH',
		'p': req.sinedria.pektis,
		'h': req.url.thesi,
	})).kinisiAdd();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η function "trapeziRefresh" επιστρέφει το array συζήτησης και το array ενεργειών
// της τελευταίας διανομής τού τραπεζιού του αιτούντος, ως properties "Z" και "E"
// αντίστοιχα.

Service.sinedria.trapeziRefresh = function(req) {
	if (req.isvoli()) return;
	if (!req.trapezi) return req.end('Ακαθόριστο τραπέζι');

	req.trapezi.trapeziEpafi();
	Service.feredata.sizitisi(req, req.trapezi.sizitisi, 'Z');
	Service.feredata.energia(req);
	req.end();
}
